<?php


namespace ffhome\frame\traits;


use think\facade\Db;

trait SaveTrait
{
    /**
     * 创建者ID的字段名称, false表示没有此功能
     * @var string|bool
     */
    protected $createByField = 'create_by';

    /**
     * 创建时间字段名称
     * @var string
     */
    protected $createTimeField = 'create_time';

    /**
     * 修改者ID的字段名称, false表示没有此功能
     * @var string|bool
     */
    protected $updateByField = 'update_by';

    /**
     * 修改者时间字段名称
     * @var string|bool
     */
    protected $updateTimeField = 'update_time';

    /**
     * 删除时记录到日志表中
     * @var bool
     */
    protected $recordDelete = true;

    /**
     * 重写验证规则
     * @param array $data
     * @param array|string $validate
     * @param array $message
     * @param bool $batch
     * @return array|bool|string|true
     */
    public function validate(array $data, $validate, array $message = [], bool $batch = false)
    {
        try {
            parent::validate($data, $validate, $message, $batch);
        } catch (\Exception $e) {
            $this->error($e->getMessage());
        }
        return true;
    }

    /**
     * 新增数据操作
     * @return mixed
     */
    public function add()
    {
        if ($this->request->isAjax()) {
            $this->addOperate();
        }
        return $this->addPage();
    }

    /**
     * 新增数据页面
     * @return mixed
     */
    protected function addPage()
    {
        $this->assignConstant();
        $this->assign('row', $this->setDefaultValueInAddPage([]));
        return $this->fetch($this->setAddPage());
    }

    /**
     * 新增数据页面中，设置数据的默认值，直接向$row数组增加数据即可，如$row['sort']=1000;
     * @param array $row
     * @return array
     */
    protected function setDefaultValueInAddPage(array $row): array
    {
        return $row;
    }

    /**
     * 设置新增数据的模板，默认'edit'，即新增与编辑使用同一模板
     * @return string
     */
    protected function setAddPage(): string
    {
        return 'edit';
    }

    /**
     * 设置修改数据的模板
     * @return string
     */
    protected function setEditPage(): string
    {
        return '';
    }

    /**
     * 新增数据操作
     */
    protected function addOperate()
    {
        $fields = $this->getAddFilterFields();
        if (!empty($fields)) {
            $data = $this->request->only($fields);
        } else {
            $data = $this->request->param();
        }
        Db::transaction(function () use (&$data) {
            $this->onBeforeAdd($data);
            $data['id'] = Db::name($this->modelName)->insertGetId($data);
            $this->onAfterAdd($data);
        });
        $this->success($this->getAddSuccessInfo($data), $data);
    }

    /**
     * 新增成功的信息
     * @param $data
     * @return string
     */
    protected function getAddSuccessInfo($data): string
    {
        return lang('common.save_success');
    }

    /**
     * 新增时的字段数组
     * @return array
     */
    protected function getAddFilterFields(): array
    {
        return $this->getFilterFields();
    }

    /**
     * 修改时的字段数组
     * @return array
     */
    protected function getEditFilterFields(): array
    {
        $fields = $this->getFilterFields();
        if (!empty($fields) && $this->updateByField !== false) {
            $fields[] = $this->updateTimeField;
        }
        return $fields;
    }

    /**
     * 保存时（含新增、修改）的字段数组
     * @return array
     */
    protected function getFilterFields(): array
    {
        return [];
    }

    /**
     * 新增操作前触发的事件，默认处理数据验证功能
     * @param array $data
     */
    protected function onBeforeAdd(array &$data)
    {
        //增加创建者与创建时间
        if ($this->createByField !== false) {
            $data[$this->createByField] = $this->getCurrentUserId();
            $data[$this->createTimeField] = date('Y-m-d H:i:s');
        }
        if ($this->updateByField !== false) {
            $data[$this->updateByField] = $this->getCurrentUserId();
            $data[$this->updateTimeField] = date('Y-m-d H:i:s');
        }
        $this->onBeforeSave($data);
        $rule = $this->validateRuleInAdd($data);
        $this->validate($data, $rule);
    }

    /**
     * 新增时的验证规则，可以通过数据创建不同的规则，也可以直接验证数据抛出异常
     * @param array $data
     * @return array
     */
    protected function validateRuleInAdd(array $data): array
    {
        return $this->validateRule($data);
    }

    /**
     * 修改时的验证规则，可以通过数据创建不同的规则，也可以直接验证数据抛出异常
     * @param array $data
     * @return array
     */
    protected function validateRuleInEdit(array $data): array
    {
        return $this->validateRule($data);
    }

    /**
     * 新增与修改时共同的验证规则，可以通过数据创建不同的规则，也可以直接验证数据抛出异常
     * @param array $data
     * @return array
     */
    protected function validateRule(array $data): array
    {
        return [];
    }

    /**
     * 新增操作后触发的事件，默认处理清除模型缓存
     * @param array $data
     */
    protected function onAfterAdd(array &$data)
    {
        $this->onAfterSave($data);
    }

    /**
     * 修改数据操作
     * @param int $id 修改数据的主键
     * @return mixed
     */
    public function edit(int $id)
    {
        $row = $this->getModelInfo($id);
        if ($this->request->isAjax()) {
            $this->editOperate($row);
        }
        return $this->editPage($row);
    }

    /**
     * 修改数据页面
     * @param array $row 数据库的原始数据
     * @return mixed
     */
    protected function editPage(array $row)
    {
        $this->assignConstant();
        $this->assign('row', $row);
        return $this->fetch($this->setEditPage());
    }

    /**
     * 修改数据操作
     * @param array $row 数据库的原始数据
     */
    protected function editOperate(array $row)
    {
        $fields = $this->getEditFilterFields();
        if (!empty($fields)) {
            $data = $this->request->only($fields);
        } else {
            $data = $this->request->param();
        }
        Db::transaction(function () use (&$data, $row) {
            $this->onBeforeEdit($data, $row);
            Db::name($this->modelName)->update($data);
            $this->onAfterEdit($data, $row);
        });
        $this->success($this->getEditSuccessInfo($data), $data);
    }

    /**
     * 修改成功的信息
     * @param $data
     * @return string
     */
    protected function getEditSuccessInfo($data): string
    {
        return lang('common.save_success');
    }

    /**
     * 修改操作前触发的事件，默认处理乐观锁，数据验证功能
     * @param array $data 修改后的数据
     * @param array $row 数据库原有数据
     */
    protected function onBeforeEdit(array &$data, array $row)
    {
        if ($this->updateByField !== false) {
            if (!empty($data[$this->updateTimeField]) && $data[$this->updateTimeField] != $row[$this->updateTimeField]) {
                $this->error(lang('common.data_overdue'));
            }
            //增加修改者与修改时间
            $data[$this->updateByField] = $this->getCurrentUserId();
            $data[$this->updateTimeField] = date('Y-m-d H:i:s');
        }
        $this->onBeforeSave($data, $row);
        $rule = $this->validateRuleInEdit($data);
        $this->validate($data, $rule);
    }

    /**
     * 修改操作后触发的事件，默认处理清除模型缓存
     * @param array $data 修改后的数据
     * @param array $row 数据库原有数据
     */
    protected function onAfterEdit(array &$data, array $row)
    {
        $this->onAfterSave($data, $row);
    }

    protected function onBeforeSave(array &$data, array $row = [])
    {
    }

    protected function onAfterSave(array &$data, array $row = [])
    {
        $this->clearCache();
    }
}